/*
  Copyright (c) 2013 Red Hat, Inc. <http://www.redhat.com>

  This file is licensed to you under your choice of the GNU Lesser
  General Public License, version 3 or any later version (LGPLv3 or
  later), or the GNU General Public License, version 2 (GPLv2), in all
  cases as published by the Free Software Foundation.
*/

#ifndef _CONFIG_H
#define _CONFIG_H
#include "config.h"
#endif

#include "call-stub.h"
#include "defaults.h"
#include "timer.h"
#include "xlator.h"
#include "jbr-messages.h"
#include "jbrc.h"
#include "statedump.h"

#define SCAR_LIMIT      20
#define HILITE(x)       ("[1;33m"x"[0m")

/*
 * The fops are actually generated by gen-fops.py; the rest was mostly copied
 * from defaults.c (commit cd253754 on 27 August 2013).
 */

enum gf_dht_mem_types_ {
        gf_mt_jbrc_private_t = gf_common_mt_end + 1,
        gf_mt_jbrc_end
};

char            *JBRC_XATTR     = "user.jbr.active";

static inline
xlator_t *
ACTIVE_CHILD (xlator_t *parent)
{
        jbrc_private_t  *priv   = parent->private;

        return priv ? priv->active : FIRST_CHILD(parent);
}

xlator_t *
next_xlator (xlator_t *this, xlator_t *prev)
{
        xlator_list_t   *trav;

        for (trav = this->children; trav; trav = trav->next) {
                if (trav->xlator == prev) {
                        return trav->next ? trav->next->xlator
                                          : this->children->xlator;
                }
        }

        return NULL;
}

void
jbrc_retry_cb (void *cb_arg)
{
        jbrc_local_t    *local  = cb_arg;

        gf_msg (__func__, GF_LOG_INFO, 0, J_MSG_RETRY_MSG,
                HILITE("retrying %p"), local);
        call_resume_wind(local->stub);
}

#pragma generate

int32_t
jbrc_forget (xlator_t *this, inode_t *inode)
{
        gf_msg_callingfn (this->name, GF_LOG_WARNING, 0, J_MSG_INIT_FAIL,
                          "xlator does not implement forget_cbk");
        return 0;
}


int32_t
jbrc_releasedir (xlator_t *this, fd_t *fd)
{
        gf_msg_callingfn (this->name, GF_LOG_WARNING, 0, J_MSG_INIT_FAIL,
                          "xlator does not implement releasedir_cbk");
        return 0;
}

int32_t
jbrc_release (xlator_t *this, fd_t *fd)
{
        gf_msg_callingfn (this->name, GF_LOG_WARNING, 0, J_MSG_INIT_FAIL,
                          "xlator does not implement release_cbk");
        return 0;
}

struct xlator_fops fops = {
        .lookup         = jbrc_lookup,
        .stat           = jbrc_stat,
        .fstat          = jbrc_fstat,
        .truncate       = jbrc_truncate,
        .ftruncate      = jbrc_ftruncate,
        .access         = jbrc_access,
        .readlink       = jbrc_readlink,
        .mknod          = jbrc_mknod,
        .mkdir          = jbrc_mkdir,
        .unlink         = jbrc_unlink,
        .rmdir          = jbrc_rmdir,
        .symlink        = jbrc_symlink,
        .rename         = jbrc_rename,
        .link           = jbrc_link,
        .create         = jbrc_create,
        .open           = jbrc_open,
        .readv          = jbrc_readv,
        .writev         = jbrc_writev,
        .flush          = jbrc_flush,
        .fsync          = jbrc_fsync,
        .opendir        = jbrc_opendir,
        .readdir        = jbrc_readdir,
        .readdirp       = jbrc_readdirp,
        .fsyncdir       = jbrc_fsyncdir,
        .statfs         = jbrc_statfs,
        .setxattr       = jbrc_setxattr,
        .getxattr       = jbrc_getxattr,
        .fsetxattr      = jbrc_fsetxattr,
        .fgetxattr      = jbrc_fgetxattr,
        .removexattr    = jbrc_removexattr,
        .fremovexattr   = jbrc_fremovexattr,
        .lk             = jbrc_lk,
        .inodelk        = jbrc_inodelk,
        .finodelk       = jbrc_finodelk,
        .entrylk        = jbrc_entrylk,
        .fentrylk       = jbrc_fentrylk,
        .rchecksum      = jbrc_rchecksum,
        .xattrop        = jbrc_xattrop,
        .fxattrop       = jbrc_fxattrop,
        .setattr        = jbrc_setattr,
        .fsetattr       = jbrc_fsetattr,
	.fallocate	= jbrc_fallocate,
	.discard        = jbrc_discard,
};

struct xlator_cbks cbks = {
};


int32_t
mem_acct_init (xlator_t *this)
{
        int     ret = -1;

        GF_VALIDATE_OR_GOTO ("jbrc", this, out);

        ret = xlator_mem_acct_init (this, gf_mt_jbrc_end + 1);

        if (ret != 0) {
                gf_msg (this->name, GF_LOG_ERROR, ENOMEM, J_MSG_MEM_ERR,
                        "Memory accounting init failed");
                return ret;
        }
out:
        return ret;
}


int32_t
jbrc_init (xlator_t *this)
{
        jbrc_private_t  *priv   = NULL;
        xlator_list_t   *trav   = NULL;

        this->local_pool = mem_pool_new (jbrc_local_t, 128);
        if (!this->local_pool) {
                gf_msg (this->name, GF_LOG_ERROR, ENOMEM, J_MSG_MEM_ERR,
                        "failed to create jbrc_local_t pool");
                goto err;
        }

        priv = GF_CALLOC (1, sizeof (*priv), gf_mt_jbrc_private_t);
        if (!priv) {
                goto err;
        }

        for (trav = this->children; trav; trav = trav->next) {
                ++(priv->n_children);
        }

        priv->active = FIRST_CHILD(this);
        this->private = priv;
        return 0;

err:
        if (priv) {
                GF_FREE(priv);
        }
        return -1;
}

void
jbrc_fini (xlator_t *this)
{
        GF_FREE(this->private);
}

int
jbrc_get_child_index (xlator_t *this, xlator_t *kid)
{
        xlator_list_t   *trav;
        int             retval = -1;

        for (trav = this->children; trav; trav = trav->next) {
                ++retval;
                if (trav->xlator == kid) {
                        return retval;
                }
        }

        return -1;
}

uint8_t
jbrc_count_up_kids (jbrc_private_t *priv)
{
        uint8_t         retval  = 0;
        uint8_t         i;

        for (i = 0; i < priv->n_children; ++i) {
                if (priv->kid_state & (1 << i)) {
                        ++retval;
                }
        }

        return retval;
}

int32_t
jbrc_notify (xlator_t *this, int32_t event, void *data, ...)
{
        int32_t           ret        = 0;
        int32_t           index      = 0;
        jbrc_private_t   *priv       = NULL;

        GF_VALIDATE_OR_GOTO (THIS->name, this, out);
        priv = this->private;
        GF_VALIDATE_OR_GOTO (this->name, priv, out);

        switch (event) {
        case GF_EVENT_CHILD_UP:
                index = jbrc_get_child_index(this, data);
                if (index >= 0) {
                        priv->kid_state |= (1 << index);
                        priv->up_children = jbrc_count_up_kids(priv);
                        gf_msg (this->name, GF_LOG_INFO, 0, J_MSG_GENERIC,
                                "got CHILD_UP for %s, now %u kids",
                                ((xlator_t *)data)->name,
                                priv->up_children);
                }
                ret = default_notify (this, event, data);
                break;
        case GF_EVENT_CHILD_DOWN:
                index = jbrc_get_child_index(this, data);
                if (index >= 0) {
                        priv->kid_state &= ~(1 << index);
                        priv->up_children = jbrc_count_up_kids(priv);
                        gf_msg (this->name, GF_LOG_INFO, 0, J_MSG_GENERIC,
                                "got CHILD_DOWN for %s, now %u kids",
                                ((xlator_t *)data)->name,
                                priv->up_children);
                }
                break;
        default:
                ret = default_notify (this, event, data);
        }

out:
        return ret;
}

int
jbrc_priv_dump (xlator_t *this)
{
        jbrc_private_t     *priv = NULL;
        char                key_prefix[GF_DUMP_MAX_BUF_LEN];
        xlator_list_t      *trav = NULL;
        int32_t             i    = -1;

        GF_VALIDATE_OR_GOTO (THIS->name, this, out);
        priv = this->private;
        GF_VALIDATE_OR_GOTO (this->name, priv, out);

        snprintf(key_prefix, GF_DUMP_MAX_BUF_LEN, "%s.%s",
                 this->type, this->name);
        gf_proc_dump_add_section(key_prefix);

        gf_proc_dump_write("up_children", "%u", priv->up_children);

        for (trav = this->children, i = 0; trav; trav = trav->next, i++) {
                snprintf(key_prefix, GF_DUMP_MAX_BUF_LEN, "child_%d", i);
                gf_proc_dump_write(key_prefix, "%s", trav->xlator->name);
        }

out:
        return 0;
}

struct xlator_dumpops dumpops = {
        .priv       = jbrc_priv_dump,
};

class_methods_t class_methods = {
        .init           = jbrc_init,
        .fini           = jbrc_fini,
        .notify         = jbrc_notify,
};

struct volume_options options[] = {
	{ .key = {NULL} },
};
